--用2*2行列键盘实现切换歌曲，选择上一首，选择下一首，暂停/恢复
--用软件行列键盘，底层已经做好防抖了，如果用单独按键，自行处理    key_in = {pin.PD13, pin.PD12}  key_out = {pin.PC5, pin.PC4}
--当前选择上/下一首不会立刻生效，按切换歌曲才会生效，如果需要立刻生效，就取消相应注释即可

--[[接线要求:

SPI 使用常规4线解法
Air105开发板         TF模块
PC9                  CS
PB12(SPI0_CLK)       CLK
PB14(SPI0_MISO)      MOSI
PB15(SPI0_MISO)      MISO
5V                   VCC
GND                  GND
]]
-- music文件夾，可以換成你自己的目录，不用加/sd/，demo里固定用TF卡目录，如果要本地目录，自行修改
 musicDir = "/music/"
local taskName = "task_audio"

local playList = {}
local curPlay = 0
local prePlay = 0
local nextPlay = 1
local tFiles = 0
local isPause = false
local MSG_MD = "moreData"   -- 播放缓存有空余
local MSG_PD = "playDone"   -- 播放完成所有数据
local MSG_NEW = "audioNew"  -- 播放新的歌曲

audio.on(0, function(id, event)
    if event == audio.MORE_DATA then
        sysplus.sendMsg(taskName, MSG_MD)
    else
        sysplus.sendMsg(taskName, MSG_PD)
    end
end)

--[[
--旋转编码器中间下压按键部分

local button_timer_outtime = 10 --按键定时器: 10ms
local button_shake_time = 1     --按键消抖时间: button_shake_time*button_timer_outtime
local button_long_time =50    --按键长按时间: button_shake_time*button_timer_outtime
local fuck_time = 200
local button_detect = true
local button_state = false
local button_cont = 0

button = gpio.setup(pin.PC7, 
function() 
    if not button_detect then return end
    button_detect = false
    button_state = true
end, 
gpio.PULLUP,gpio.FALLING)

button_timer = sys.timerLoopStart(function()
    if button_state then
        if button() == 0 then
            button_cont = button_cont + 1
            if button_cont > fuck_time then
                print("long pass 1")
                moveNext()
          -- lcd_music_munt()
            end
        else 
            if button_cont < button_shake_time then
            else
                if button_cont < button_long_time then
                    print("pass")
                    stop()
                elseif (button_cont > button_long_time) and  (button_cont < fuck_time)  then
                    print("long pass")
                    ok()
                end
            end
            button_cont = 0
            button_state = false
            button_detect = true
        end
    end
end,button_timer_outtime) 

-- 旋转编码器旋钮部分
-- 正反方向根据实际情况决定
local s1_pin = pin.PA8
local s2_pin = pin.PA9
local s1_last = nil
local s2_last = nil
local s1_up = false
local s2_up = false
local s1_down = false
local s2_down = false
local _,us_tick = mcu.tick64()
us_tick = us_tick * 1000 --低于1ms的当杂波去除，可以根据实际需要修改

local function stage1()
    pin_need = 0
    -- log.info("wait down")
    ioqueue.exti(s1_pin, nil, gpio.FALLING, true)
    ioqueue.exti(s2_pin, nil, gpio.FALLING, true)
end

local function stage2()
    pin_need = 1
    -- log.info("wait up")
    ioqueue.exti(s1_pin, nil, gpio.RISING, true)
    ioqueue.exti(s2_pin, nil, gpio.RISING, true)
    s1_up,s2_up,s1_down,s2_down = false,false,false,false
end

local function s1_irq(val, tick)
    if gpio.get(s1_pin) ~= pin_need then return end
    if s1_last then
        local result,_ = mcu.dtick64(tick, s1_last, us_tick)
        if result then
            -- log.info("s1", mcu.dtick64(tick, s1_last))
            if pin_need > 0 then
                s1_up = true
            else
                s1_down = true
            end
            -- log.info("s1", s1_up, s1_down)
        else
            return
        end
    else
        -- log.info("s1")
        s1_up = true
    end
    if pin_need > 0 and s1_up and s2_up then
        stage1()
    end
    if s2_down then
        log.info("1 s2pin先检测到，右转+1")
        moveNext()
        sys.publish("next", ture)
      --  lcd_music_munt()
        -- print("按键2-2")
        stage2()
     
    end
    s1_last = tick

end

local function s2_irq(val, tick)
    if gpio.get(s2_pin) ~= pin_need then return end
    if s2_last then
        local result,_ = mcu.dtick64(tick, s2_last, us_tick)
        if result then 
            -- log.info("s2", mcu.dtick64(tick, s2_last))
            if pin_need > 0 then
                s2_up = true
            else
                s2_down = true
            end
            -- log.info("s2", s2_up, s2_down)
        else
            return
        end
    else
        -- log.info("s2")
        s2_check = true
    end
    if pin_need > 0 and s1_up and s2_up then
        stage1()
    end
    if s1_down then
        log.info("1 s1pin先检测到，左转+1")
        movePre()
      --  lcd_music_munt()
      --  print("按键2-1")
        stage2()
       
    end
    s2_last = tick
end

sys.subscribe("IO_QUEUE_EXTI_" .. s1_pin, s1_irq)
sys.subscribe("IO_QUEUE_EXTI_" .. s2_pin, s2_irq)

]]


--LCD打印上下一曲区域
function rotary_start()
    pin_need = 1
    ioqueue.exti(s1_pin, nil, gpio.RISING, true)
    ioqueue.exti(s2_pin, nil, gpio.RISING, true)
    -- sys.timerLoopStart(clear, 10000)
end

 function lcd_music_munt()
    
    local loadx1 = 120
    local loadx2 = loadx1 + 30
    local loadx3 = loadx1 + 20
    lcd.fill(0,loadx1,320,loadx2)
    lcd.drawStr(0,loadx3,"上一曲: "..(prePlay+1).."/"..tFiles..":"..playList[prePlay],0XFF9999)
   
    local loadx1 = loadx1 + 30
    local loadx2 = loadx1 + 30
    local loadx3 = loadx1 + 20
    lcd.fill(0,loadx1,320,loadx2,0Xfaff72)
    lcd.drawStr(0,loadx3,"选   中: "..(curPlay+1).."/"..tFiles..":"..playList[curPlay],0XFF9999)

    local loadx1 = loadx1 + 30
    local loadx2 = loadx1 + 30
    local loadx3 = loadx1 + 20
    lcd.fill(0,loadx1,320,loadx2)
    lcd.drawStr(0,loadx3,"下一曲: "..(nextPlay+1).."/"..tFiles..":"..playList[nextPlay],0XFF9999)

end

    

--换曲逻辑

 function moveNext()
    prePlay = curPlay
    curPlay = curPlay + 1
    if curPlay >= tFiles then
        curPlay = 0
    end
    nextPlay = curPlay + 1
    if nextPlay >= tFiles then
        nextPlay = 0
    end

    lcd_music_munt()
    log.info("选择下一首")
    log.info("上一首", prePlay, playList[prePlay])
    log.info("选择", curPlay, playList[curPlay])
    log.info("下一首", nextPlay, playList[nextPlay])
end

 function movePre()
    nextPlay = curPlay
    curPlay = prePlay
    if prePlay > 0 then
        prePlay = prePlay - 1
    else
        prePlay = tFiles - 1
    end

    lcd_music_munt()
    log.info("选择上一首")
    log.info("上一首", prePlay, playList[prePlay])
    log.info("选择", curPlay, playList[curPlay])
    log.info("下一首", nextPlay, playList[nextPlay])
end

local function task_cb(msg)
    log.info(msg[1], msg[2], msg[3], msg[4])
end

function stop()
    isPause = not isPause
    audio.pause(0, isPause)
    log.info("暂停状态", isPause)
end

function ok()
    sysplus.sendMsg(taskName, MSG_NEW)
    lcd_music_munt()
end

-- 用软件行列键盘，底层已经做好防抖了，如果用单独按键，自行处理
sys.subscribe("SOFT_KB_INC", function(port, data, state)
    -- log.info(data, state)
    -- 按下起效
   
    if state > 0 then
        
        if data == 0 then
            print("按键1-1")
            stop()
        elseif data == 1 then
            -- 开始播放或者立刻结束当前歌曲并播放下一首
           
        print ("按键1-2")
        ok()
          --  lcd_music_munt()
        elseif data == 16 then
            
            print("按键2-1")

            movePre()
           -- lcd_music_munt()
            -- 如果需要立刻就切换歌曲就取消注释
       --  sysplus.sendMsg(taskName, MSG_NEW)
        elseif data == 17 then
            
           print("按键2-2")

           moveNext()
        --   lcd_music_munt()
            -- 如果需要立刻就切换歌曲就取消注释
          --sysplus.sendMsg(taskName, MSG_NEW)
        
        end
    end
end)

local function audio_task()

    --初始化矩阵键盘部分
   --[[key_in = {pin.PB3, pin.PB2}
    key_out = {pin.PB5, pin.PC0}
]]
    --[[key_in = {pin.PC0, pin.PB5}
    key_out = {pin.PB2, pin.PB3}
]]
    --[[key_in = {pin.PC4, pin.PC5}
    key_out = {pin.PD12, pin.PD13}
]]
    key_in = {pin.PD13, pin.PD12}
    key_out = {pin.PC5, pin.PC4}
    softkb.init(0, key_in, key_out)

--初始化SD卡部分，SD卡初始部分我是用的底部SPI2接线，具体情况根据自己使用修改
    local tagLen = 0
    local frameLen = 1152 * 4
    local spiId = 0
    local nowPlay
    local result = spi.setup(
        spiId,--串口id
        255, -- 不使用默认CS脚
        0,--CPHA
        0,--CPOL
        8,--数据宽度
        400*1000  -- 初始化时使用较低的频率
    )
    local isMP3 = false
    local TF_CS = pin.PB13
    gpio.setup(TF_CS, 1)
    -- fatfs.debug(1) -- 若挂载失败,可以尝试打开调试信息,查找原因
    fatfs.mount("SD", spiId, TF_CS, 24000000)
    -- fatfs.mount("SD", 0, TF_CS, 24000000)

    local data, err = fatfs.getfree("SD")
    local buff = zbuff.create(1024)
    local result, AudioFormat, NumChannels, SampleRate, BitsPerSample, is_signed
    local decoder, msg, isRun
    if data then
        log.info("fatfs", "getfree", json.encode(data))
        local dir_nums, dir_info = fatfs.lsdir(musicDir)
        for k,v in pairs(dir_info) do
            if k:find(".mp3") or k:find(".MP3") or k:find(".wav") or k:find(".WAV") then
                log.info("找到",k)
                playList[tFiles] = k
                tFiles = tFiles + 1
            end
        end
        log.info("总共", tFiles)
        prePlay = tFiles - 1
        log.info("等待切换歌曲")
        sysplus.waitMsg(taskName, MSG_NEW)
        while true do
            nowPlay = curPlay
            log.info("开始播放")
            log.info("上一首", prePlay, playList[prePlay])
            log.info("当前", curPlay, playList[curPlay])
            log.info("下一首", nextPlay, playList[nextPlay])

--显示正在播放音频部分
            lcd.fill(0,70,320,100,0Xe3f9fd)
            lcd.drawStr(0,90,"播放中"..(curPlay+1).."/"..tFiles..":"..playList[curPlay],0XFF9999)
            lcd_music_munt()

            if playList[curPlay]:find(".mp3") or playList[curPlay]:find(".MP3") then
                
                -- 解析MP3的必要信息
                decoder = codec.create(codec.MP3)
            else
                decoder = codec.create(codec.WAV)
            end
            result, AudioFormat, NumChannels, SampleRate, BitsPerSample, is_signed = codec.info(decoder, "/sd".. musicDir .. playList[curPlay])
            if result then
                log.info("audio info", NumChannels, SampleRate, BitsPerSample)
                buff:resize(65536)
                --先解码出一段数据
                result = codec.data(decoder, buff)
                --启动播放
                log.debug("start", audio.start(0, AudioFormat, NumChannels, SampleRate, BitsPerSample, is_signed))
                --后续解码出一段数据后，让音频通道内始终保持有缓存数据，播放才能连续
                audio.write(0, buff)
                result = codec.data(decoder, buff) 
                audio.write(0, buff)
                isRun = true
                --等待音频通道的回调消息，或者切换歌曲的消息
                while true do
                    msg = sysplus.waitMsg(taskName, nil, 2000)
                    if type(msg) == 'table' then
                        if msg[1] == MSG_MD and isRun then
                            result = codec.data(decoder, buff)
                            if not result then
                                log.info("没有数据了")
                                isRun = false
                            else
                                audio.write(0, buff)
                            end
                        elseif msg[1] == MSG_PD then
                            log.info("播放结束")
                            break
                        elseif msg[1] == MSG_NEW then
                            log.info("切换歌曲")
                            break
                        end
                    else
                        if not isPause then
                            log.error(type(msg), msg)
                        end
                    end
                end
            else
                log.debug("/sd".. musicDir .. playList[curPlay], "解码失败!")

                sys.wait(1000)
            end
            audio.stop(0)
            codec.release(decoder)
            decoder = nil
            -- 如果有底层的播放消息，也释放掉
            sysplus.waitMsg(taskName, MSG_MD, 10)
            sysplus.waitMsg(taskName, MSG_PD, 10)
            isPause = false
            log.info(rtos.meminfo("sys"))
            log.info(rtos.meminfo("lua"))
            if nowPlay == curPlay then
                prePlay = curPlay
                curPlay = curPlay + 1
                if curPlay >= tFiles then
                    curPlay = 0
                end
                nextPlay = curPlay + 1
                if nextPlay >= tFiles then
                    nextPlay = 0
                end
            end
        end
    else
        log.info("fatfs", "err", err)
    end
    sysplus.taskDel(taskName)
end

sysplus.taskInitEx(audio_task, taskName, task_cb)